1.1.5 react 父子组件通信

React 数据流动是单向的; 父组件向子组件通信: props
子组件向父组件通信: 回调函数/自定义事件
跨级组件通信: 层层组件传递props/context
没有嵌套关系组件之间的通信: 自定义事件

父组件向子组件通信

通过 props

# 1.父组件调用子组件传递参数

# parent.js

class Parent extends Component {
  render() {
    return (
      <div>
          <Child name="testparam" />
      </div>
    );
  }
}


# child.js

import React from 'react';
import PropTypes from 'prop-types';

class Child extends Component  {
  render() {
      return (
          <div>{this.props.name}</div>
      );
  }
}

Child.propTypes = {
    name: PropTypes.string.isRequired
};

export default Child;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

2.子组件向父组件通信

利用回调函数
利用自定义事件机制

父组件中: 

hideConponent() {
  this.setState({
    isShowChild: false,
  });
}
render() {
  return (
    <div>
      <child hideConponent={this.hideConponent.bind(this)} />
    </div>
  );
}


子组件中:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Child extends Component {
  static propTypes = {
    hideConponent: PropTypes.func.isRequired,
  }
  render() {  
    return (
      <div>
        this is child
        <button onClick={this.props.hideConponent}>点击 child </button>
      </div>
    );
  }
}

export default Child;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  • 子组件传值到父组件
父组件中: 

hideConponent(val) {
  console.log(val);
  this.setState({
    isShowChild: false,
  });
}
render() {
  return (
    <div>
      <child hideConponent={this.hideConponent.bind(this)} />
    </div>
  );
}


子组件中:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Child extends Component {
  static propTypes = {
    hideConponent: PropTypes.func.isRequired,
  }
  handleConponent() {
    let val = '123';
    this.props.hideConponent(val);
  }
  render() {  
    return (
      <div>
        this is child
        <button onClick={this.handleConponent.bind(this)}>点击 child </button>
      </div>
    );
  }
}

export default Child;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

3.跨级组件通信

当需要让子组件跨级访问信息时,我们可以像之前说的方法那样向更高级别的组件层层传递 props,
但此时的代码显得不那么优雅,甚至有些冗余。
在 React 中,我们还可以使用 context 来 实现跨级父子组件间的通信;

不过 React 官方并不建议大量使用 context,因为尽管它可以减少逐层传递,
但当组件结 构复杂的时候,我们并不知道 context 是从哪里传过来的。
Context 就像一个全局变量一样,而 全局变量正是导致应用走向混乱的罪魁祸首之一,
给组件带来了外部依赖的副作用。

# 父组件

  // 提供一个函数,用来返回相应的context对象
  getChildContext() {
    return {
      color: 'red'
    };
  }

  render() {}

  HomeIndex.childContextTypes = {
    color: PropTypes.string
  };

childContextTypes必须得设置,不设置在各下级组件中无法获取到数据,这项是必须项;

不设置会报这样的错误:Index.getChildContext(): childContextTypes must be defined in order to use getChildContext().



# 子组件

Child.contextTypes = {
  color: PropTypes.string
};

render() {
    return (
      <div>
        <button>child context {this.context.color} </button>
      </div>
    );
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

子组件在《深入 React 技术栈》 中看到使用

static contextTypes = {    
  color: PropTypes.string   
}; 
1
2
3

可是我这里报错, 如果 babel设置为 es6 的转码方式,会报错,因为定义静态属性不属于 es6,而在 es7 的草案中。
ES6 的 class 中只有静态方法,没有静态属性。
//由于是用ES6 class语法创建组件,其内部只允许定义方法,而不能定义属性,
class的属性只能定义在 class 之外。 所以 contextTypes 要写在组件外部。

Child.contextTypes = {
  color: PropTypes.string
};
1
2
3

4.没有嵌套关系的组件通信

使用自定义事件机制

没有嵌套关系的,那只能通过可以影响全局的一些机制去考虑。刚才讲到的自定义事件机制 不失为一种上佳的方法。
我们就以常用的发布/订阅模式来举例,这里借用 Node.js Events 模块的浏览器版实现。

import { EventEmitter } from 'events'; 
export default new EventEmitter(); 

# 然后把 EventEmitter 实例输出到各组件中使用: 

import ReactDOM from 'react-dom';
import React, { Component, PropTypes } from 'react';
import emitter from './events';
class ListItem extends Component {
  static defaultProps = { checked: false, }
  constructor(props) { super(props); }
  render() { 
    return (
      <li>
        <input type="checkbox" checked={this.props.checked} onChange={this.props.onChange} />
        <span>{this.props.value}</span>
      </li>
    ); 
  }
}
class List extends Component {
  constructor(props) {
    super(props);
    this.state = { 
      list: this.props.list.map(entry => ({ 
        text: entry.text, 
        checked: entry.checked || false, 
      })), 
    };
  }
  onItemChange(entry) {
    const { list } = this.state;
    this.setState({ 
      list: list.map(prevEntry => ({ 
        text: prevEntry.text, 
        checked: prevEntry.text === entry.text ? !prevEntry.checked : prevEntry.checked, 
      }))
    });
    emitter.emit('ItemChange', entry);
  }
  render() { 
    return (
      <div>
        <ul>
          {this.state.list.map((entry, index) => (   
            <ListItem  
              key={`list-${index}`}  
              value={entry.text}  
              checked={entry.checked}  
              onChange={this.onItemChange.bind(this, entry)}   />    
          ))}  
        </ul>
      </div>);
    }
}
class App extends Component {
  componentDidMount() { 
    this.itemChange = emitter.on('ItemChange', (data) => { 
      console.log(data); 
    });
  }
  componentWillUnmount() { 
    emitter.removeListener(this.itemChange); 
  }
  render() { 
    return (<List list={[{text: 1}, {text: 2}]} />); 
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

参考